﻿using Lhz.Common.Business;
using Lhz.Data;
using Lhz.Framework.Test.Entity;
using Lhz.InternetService;
using Lhz.Test.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;

namespace Lhz.Framework.Test.LhzCommonService
{
    [TestClass]
    public class UnitTestEntityService
    {
        private EntityProxy<Guid, Student> epStudent;
        private EntityProxy<Guid, Teacher> epTeacher;
        private List<Student> students;
        private List<Teacher> teachers;
        private List<Book> books;
        private string urlStudent = "http://localhost:53843/ServiceStudent.svc";
        private string urlBook = "http://localhost:53843/ServiceBook.svc";
        private string urlTeacher = "http://localhost:53843/ServiceTeacher.svc";
        private ServiceInvoker si = new ServiceInvoker(typeof(Pagination), typeof(OrderBy), typeof(Student));

        public UnitTestEntityService()
        {
            epStudent = new EntityProxy<Guid, Student>();
            epTeacher = new EntityProxy<Guid, Teacher>();
            ClearData();
        }

        private void AddData(int count = 5)
        {
            students = new List<Student>();
            teachers = new List<Teacher>();
            for (int i = 0; i < count; i++)
            {
                teachers.Add(EntityFactory.BuildTeacher());
            }
            epTeacher.Add(teachers);
            for (int i = 0; i < count; i++)
            {
                students.Add(EntityFactory.BuildStudent(teachers[i]));
            }
            books = new List<Book>();
            for (int i = 0; i < students.Count; i++)
            {
                var student = students[i];
                student.Teachers.Add(teachers[i]);
                for (int j = 0; j < 5; j++)
                {
                    student.Books.Add(EntityFactory.BuildBook(student));
                }
                for (int j = 0; j < 5; j++)
                {
                    var teacher = EntityFactory.BuildTeacher();
                    epTeacher.Add(teacher);
                    student.Teachers.Add(teacher);
                }
                student.HeadteacherGuid = teachers[i].Guid;
                student.Headteacher = teachers[i];
            }
            epStudent.Add(students);
        }

        private void ClearData()
        {
            epStudent.Delete(epStudent.Query());
            epTeacher.Delete(epTeacher.Query());
        }

        [TestMethod]
        public void EntityServiceTestMethodCrud()
        {
            var headteacher = EntityFactory.BuildTeacher();
            epTeacher.Add(headteacher);
            var student = EntityFactory.BuildStudent(headteacher);
            Dictionary<string, object> changes = new Dictionary<string, object>();
            changes.Add("Name", "Liu Hongzhe");
            var pct = new ServiceCrudTester<Guid, Student>(urlStudent);
            pct.CrudTest(student, student.Guid, changes, new string[] { "Teachers", "Books", "Headteacher" });
        }

        [TestMethod]
        public void EntityServiceTestMethodCrudInTransaction()
        {
            var headteacher = EntityFactory.BuildTeacher();
            epTeacher.Add(headteacher);
            var student = EntityFactory.BuildStudent(headteacher);
            Dictionary<string, object> changes = new Dictionary<string, object>();
            changes.Add("Name", "Liu Hongzhe");
            var pct = new ServiceCrudTester<Guid, Student>(urlStudent);
            using (var ts = new TransactionScope())
            {
                pct.CrudTest(student, student.Guid, changes, new string[] { "Teachers", "Books", "Headteacher" });
                ts.Complete();
            }
        }

        [TestMethod]
        public void EntityServiceTestMethodPagination()
        {
            AddData(35);
            var pagination = new Pagination() { Index = 3, Size = 10 };
            var arguments = new object[] { pagination, new string[] { } };
            var serviceStudents = si.Invoke<Student[]>(urlStudent, "QueryByPagination", arguments);
            Assert.AreEqual(10, serviceStudents.Length);
            Assert.AreEqual(students.Count, (arguments[0] as Pagination).Count);
            pagination = new Pagination() { Index = 4, Size = 10 };
            arguments = new object[] { pagination, new string[] { } };
            serviceStudents = si.Invoke<Student[]>(urlStudent, "QueryByPagination", arguments);
            Assert.AreEqual(5, serviceStudents.Length);
            Assert.AreEqual(students.Count, (arguments[0] as Pagination).Count);
            ClearData();
        }

        [TestMethod]
        public void EntityServiceTestMethodOrderBy()
        {
            AddData();
            var orderBies = new OrderBy[2];
            orderBies[0] = new OrderBy() { PropertyName = "Guid", OrderByMode = OrderByMode.Asc };
            orderBies[1] = new OrderBy() { PropertyName = "Name", OrderByMode = OrderByMode.Desc };
            var serviceStudents = si.Invoke<Student[]>(urlStudent, "QueryByOrderBies", orderBies, new string[] { });
            for (int i = 0; i < serviceStudents.Length - 2; i++)
            {
                var currentStudent = serviceStudents[i];
                var nextStudent = serviceStudents[i + 1];
                var compareGuid = string.Compare(currentStudent.Guid.ToString(), nextStudent.Guid.ToString());
                var compareName = string.Compare(currentStudent.Name.ToString(), nextStudent.Name.ToString());
                Assert.IsTrue(compareGuid < 0 || (compareGuid == 0 && compareName >= 0));
            }
            orderBies = new OrderBy[2];
            orderBies[0] = new OrderBy() { PropertyName = "Guid", OrderByMode = OrderByMode.Desc };
            orderBies[1] = new OrderBy() { PropertyName = "Name", OrderByMode = OrderByMode.Asc };
            serviceStudents = si.Invoke<Student[]>(urlStudent, "QueryByOrderBies", orderBies, new string[] { });
            for (int i = 0; i < serviceStudents.Length - 2; i++)
            {
                var currentStudent = serviceStudents[i];
                var nextStudent = serviceStudents[i + 1];
                var compareGuid = string.Compare(currentStudent.Guid.ToString(), nextStudent.Guid.ToString());
                var compareName = string.Compare(currentStudent.Name.ToString(), nextStudent.Name.ToString());
                Assert.IsTrue(compareGuid > 0 || (compareGuid == 0 && compareName <= 0));
            }
            ClearData();
        }

        [TestMethod]
        public void EntityServiceTestMethodStudentWithBooks()
        {
            var teacher = EntityFactory.BuildTeacher();
            epTeacher.Add(teacher);
            var student = EntityFactory.BuildStudent(teacher);
            var books = new Dictionary<Guid, Book>();
            for (int i = 0; i < 5; i++)
            {
                var book = EntityFactory.BuildBook(student);
                books.Add(book.Guid, book);
            }
            var sctStudent = new ServiceCrudTester<Guid, Student>(urlStudent);
            var sctBook = new ServiceCrudTester<Guid, Book>(urlBook);
            sctStudent.CrudWithOneToManyTest(CascadeMode.AllDeleteOrphan, student, student.Guid, "Books", books, "StudentGuid", sctBook);
            ClearData();
        }

        [TestMethod]
        public void EntityServiceTestMethodStudentWithBooksInTransaction()
        {
            var teacher = EntityFactory.BuildTeacher();
            epTeacher.Add(teacher);
            var student = EntityFactory.BuildStudent(teacher);
            var books = new Dictionary<Guid, Book>();
            for (int i = 0; i < 5; i++)
            {
                var book = EntityFactory.BuildBook(student);
                books.Add(book.Guid, book);
            }
            var sctStudent = new ServiceCrudTester<Guid, Student>(urlStudent);
            var sctBook = new ServiceCrudTester<Guid, Book>(urlBook);
            using (var ts = new TransactionScope())
            {
                sctStudent.CrudWithOneToManyTest(CascadeMode.AllDeleteOrphan, student, student.Guid, "Books", books, "StudentGuid", sctBook);
                ts.Complete();
            }
            ClearData();
        }

        [TestMethod]
        public void EntityServiceTestMethodStudentWithTeacher()
        {
            var teachers = new Dictionary<Guid, Teacher>();
            for (int i = 0; i < 5; i++)
            {
                var teacher = EntityFactory.BuildTeacher();
                teachers.Add(teacher.Guid, teacher);
                epTeacher.Add(teacher);
            }
            var student = EntityFactory.BuildStudent(teachers.Values.ElementAt(0));
            var sctStudent = new ServiceCrudTester<Guid, Student>(urlStudent);
            var sctTeacher = new ServiceCrudTester<Guid, Teacher>(urlTeacher);
            sctStudent.CrudWithManyToManyTest(CascadeMode.None, student, student.Guid, "Teachers", teachers, sctTeacher);
            ClearData();
        }

        [TestMethod]
        public void EntityServiceTestMethodStudentWithTeacherInTransaction()
        {
            var teachers = new Dictionary<Guid, Teacher>();
            for (int i = 0; i < 5; i++)
            {
                var teacher = EntityFactory.BuildTeacher();
                teachers.Add(teacher.Guid, teacher);
                epTeacher.Add(teacher);
            }
            var student = EntityFactory.BuildStudent(teachers.Values.ElementAt(0));
            var sctStudent = new ServiceCrudTester<Guid, Student>(urlStudent);
            var sctTeacher = new ServiceCrudTester<Guid, Teacher>(urlTeacher);
            using (var ts = new TransactionScope())
            {
                sctStudent.CrudWithManyToManyTest(CascadeMode.None, student, student.Guid, "Teachers", teachers, sctTeacher);
                ts.Complete();
            }
            ClearData();
        }
    }
}
